package com.log4jviewer.filters;

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import java.util.ArrayList;
import java.util.List;

import junit.framework.Assert;

import org.junit.BeforeClass;
import org.junit.Test;

import com.log4jviewer.domain.LogEvent;
import com.log4jviewer.filters.FilterItemModel.FieldType;
import com.log4jviewer.filters.FilterItemModel.LogicOperand;

public class LogFiltrationTest {

    private static List<LogEvent> logMocks;

    private static LogFilterEngine logFilterEngine;

    @BeforeClass
    public static void init() {
        logFilterEngine = new LogFilterEngine();

        logMocks = new ArrayList<LogEvent>();
        logMocks.add(createLogMock("DEBUG", "DetClient", "Chinese is supported", "67", "13:52:58:371", "NDC 1",
                "Exception 1"));
        logMocks.add(createLogMock("DEBUG", "DetClient", "Japanese is supported", "23", "13:52:58:372", "NDC 2",
                "Exception 2"));
        logMocks.add(createLogMock("INFO", "DetServer$1", "Refreshing com.revere.det.app.DetServer$1@650646", "87",
                "13:52:58:429", "NDC 3", "Exception 3"));
        logMocks.add(createLogMock("DEBUG", "CollectionFactory", "Creating [java.util.concurrent.ConcurrentHashMap]",
                "27", "13:52:58:461", "NDC 4", "Exception 4"));
        logMocks.add(createLogMock("INFO", "XmlBeanDefinitionReader",
                "Loading XML bean definitions from class path resource", "57", "13:52:58:490", "NDC 5", "Exception 5"));
        logMocks.add(createLogMock("DEBUG", "DefaultDocumentLoader", "Using JAXP provider", "196", "13:52:58:506",
                "NDC 6", "Exception 6"));
    }

    private static LogEvent createLogMock(final String level, final String category, final String message,
            final String line, final String date, final String ndc, final String throwable) {
        LogEvent logMock = mock(LogEvent.class);
        when(logMock.getLevel()).thenReturn(level);
        when(logMock.getCategoryName()).thenReturn(category);
        when(logMock.getMessage()).thenReturn(message);
        when(logMock.getLineNumber()).thenReturn(line);
        when(logMock.getDate()).thenReturn(date);
        when(logMock.getNdc()).thenReturn(ndc);
        when(logMock.getThrowableInfo()).thenReturn(throwable);
        return logMock;
    }

    private static FilterItemModel createFilterItemMock(final LogicOperand logicOperand, final FieldType fieldType,
            final boolean include, final String matchPtr, final boolean enable) {
        FilterItemModel filterItemMock = mock(FilterItemModel.class);
        when(filterItemMock.getLogicOperand()).thenReturn(logicOperand);
        when(filterItemMock.getFieldType()).thenReturn(fieldType);
        when(filterItemMock.isInclude()).thenReturn(include);
        when(filterItemMock.getPattern()).thenReturn(matchPtr);
        when(filterItemMock.isEnabled()).thenReturn(enable);
        return filterItemMock;
    }

    private static FilterModel removeDisabledItems(final FilterModel filterModel) {
        for (int i = 0; i < filterModel.getItemsCount(); i++) {
            // if the item is enabled than add it to filter
            if (!filterModel.getItem(i).isEnabled()) {
                filterModel.removeItem(i);
            }
        }
        return filterModel;
    }

    @Test
    public void filterTest1() {
        FilterModel filterModel = new FilterModel();
        filterModel.addItem(createFilterItemMock(FilterItemModel.LogicOperand.AND, FilterItemModel.FieldType.LEVEL,
                true, "INFO", true));
        removeDisabledItems(filterModel);
        logFilterEngine.setFilter(filterModel);
        Assert.assertFalse(logFilterEngine.isFiltered(logMocks.get(0)));
        Assert.assertFalse(logFilterEngine.isFiltered(logMocks.get(1)));
        Assert.assertTrue(logFilterEngine.isFiltered(logMocks.get(2)));
        Assert.assertFalse(logFilterEngine.isFiltered(logMocks.get(3)));
        Assert.assertTrue(logFilterEngine.isFiltered(logMocks.get(4)));
        Assert.assertFalse(logFilterEngine.isFiltered(logMocks.get(5)));
    }

    @Test
    public void filterTest2() {
        FilterModel filterModel = new FilterModel();
        filterModel.addItem(createFilterItemMock(FilterItemModel.LogicOperand.AND, FilterItemModel.FieldType.MESSAGE,
                true, "from class", true));
        removeDisabledItems(filterModel);
        logFilterEngine.setFilter(filterModel);
        Assert.assertFalse(logFilterEngine.isFiltered(logMocks.get(0)));
        Assert.assertFalse(logFilterEngine.isFiltered(logMocks.get(1)));
        Assert.assertFalse(logFilterEngine.isFiltered(logMocks.get(2)));
        Assert.assertFalse(logFilterEngine.isFiltered(logMocks.get(3)));
        Assert.assertTrue(logFilterEngine.isFiltered(logMocks.get(4)));
        Assert.assertFalse(logFilterEngine.isFiltered(logMocks.get(5)));
    }

    @Test
    public void caseInsensitiveTest() {
        FilterModel filterModel = new FilterModel();
        filterModel.addItem(createFilterItemMock(FilterItemModel.LogicOperand.AND, FilterItemModel.FieldType.MESSAGE,
                true, "CLASS", true));
        removeDisabledItems(filterModel);
        logFilterEngine.setFilter(filterModel);
        Assert.assertFalse(logFilterEngine.isFiltered(logMocks.get(0)));
        Assert.assertFalse(logFilterEngine.isFiltered(logMocks.get(1)));
        Assert.assertFalse(logFilterEngine.isFiltered(logMocks.get(2)));
        Assert.assertFalse(logFilterEngine.isFiltered(logMocks.get(3)));
        Assert.assertTrue(logFilterEngine.isFiltered(logMocks.get(4)));
        Assert.assertFalse(logFilterEngine.isFiltered(logMocks.get(5)));
    }

    @Test
    public void allMatchingTest() {
        FilterModel filterModel = new FilterModel();
        filterModel.addItem(createFilterItemMock(FilterItemModel.LogicOperand.AND, FilterItemModel.FieldType.MESSAGE,
                true, ".*", true));
        removeDisabledItems(filterModel);
        logFilterEngine.setFilter(filterModel);
        Assert.assertTrue(logFilterEngine.isFiltered(logMocks.get(0)));
        Assert.assertTrue(logFilterEngine.isFiltered(logMocks.get(1)));
        Assert.assertTrue(logFilterEngine.isFiltered(logMocks.get(2)));
        Assert.assertTrue(logFilterEngine.isFiltered(logMocks.get(3)));
        Assert.assertTrue(logFilterEngine.isFiltered(logMocks.get(4)));
        Assert.assertTrue(logFilterEngine.isFiltered(logMocks.get(5)));
    }

    @Test
    public void filterTest3() {
        FilterModel filterModel = new FilterModel();
        filterModel.addItem(createFilterItemMock(FilterItemModel.LogicOperand.AND, FilterItemModel.FieldType.CATEGORY,
                true, "DetServer$1", true));
        filterModel.addItem(createFilterItemMock(FilterItemModel.LogicOperand.OR, FilterItemModel.FieldType.LINE, true,
                "196", true));
        filterModel.addItem(createFilterItemMock(FilterItemModel.LogicOperand.AND, FilterItemModel.FieldType.LEVEL,
                true, "DEBUG", true));
        removeDisabledItems(filterModel);
        logFilterEngine.setFilter(filterModel);
        Assert.assertFalse(logFilterEngine.isFiltered(logMocks.get(0)));
        Assert.assertFalse(logFilterEngine.isFiltered(logMocks.get(1)));
        Assert.assertTrue(logFilterEngine.isFiltered(logMocks.get(2)));
        Assert.assertFalse(logFilterEngine.isFiltered(logMocks.get(3)));
        Assert.assertFalse(logFilterEngine.isFiltered(logMocks.get(4)));
        Assert.assertTrue(logFilterEngine.isFiltered(logMocks.get(5)));
    }

    @Test
    public void filterTest4() {
        FilterModel filterModel = new FilterModel();
        filterModel.addItem(createFilterItemMock(FilterItemModel.LogicOperand.AND, FilterItemModel.FieldType.LEVEL,
                false, "INFO", true));
        filterModel.addItem(createFilterItemMock(FilterItemModel.LogicOperand.AND, FilterItemModel.FieldType.DATE,
                true, "13:52:58:506", true));
        removeDisabledItems(filterModel);
        logFilterEngine.setFilter(filterModel);
        Assert.assertFalse(logFilterEngine.isFiltered(logMocks.get(0)));
        Assert.assertFalse(logFilterEngine.isFiltered(logMocks.get(1)));
        Assert.assertFalse(logFilterEngine.isFiltered(logMocks.get(2)));
        Assert.assertFalse(logFilterEngine.isFiltered(logMocks.get(3)));
        Assert.assertFalse(logFilterEngine.isFiltered(logMocks.get(4)));
        Assert.assertTrue(logFilterEngine.isFiltered(logMocks.get(5)));
    }

    @Test
    public void filterTest5() {
        FilterModel filterModel = new FilterModel();
        filterModel.addItem(createFilterItemMock(FilterItemModel.LogicOperand.AND, FilterItemModel.FieldType.NDC, true,
                "NDC 4", true));
        filterModel.addItem(createFilterItemMock(FilterItemModel.LogicOperand.OR, FilterItemModel.FieldType.THROWABLE,
                true, "Exception 1", true));
        filterModel.addItem(createFilterItemMock(FilterItemModel.LogicOperand.OR, FilterItemModel.FieldType.LEVEL,
                true, "INFO", false));
        removeDisabledItems(filterModel);
        logFilterEngine.setFilter(filterModel);
        Assert.assertTrue(logFilterEngine.isFiltered(logMocks.get(0)));
        Assert.assertFalse(logFilterEngine.isFiltered(logMocks.get(1)));
        Assert.assertFalse(logFilterEngine.isFiltered(logMocks.get(2)));
        Assert.assertTrue(logFilterEngine.isFiltered(logMocks.get(3)));
        Assert.assertFalse(logFilterEngine.isFiltered(logMocks.get(4)));
        Assert.assertFalse(logFilterEngine.isFiltered(logMocks.get(5)));
    }
}
